package pt.utl.ist.isr.livetrack.tracker.mht;

import eu.anorien.mhl.Fact;
import org.apache.log4j.Logger;
import pt.utl.ist.isr.livetrack.server.interfaces.Zone;
import pt.utl.ist.isr.livetrack.tracker.Entity;

/**
 *
 * @author David Miguel Antunes <davidmiguel [ at ] antunes.net>
 */
public class EntityInFact<TargetInfo> implements Fact {

    private static final Logger logger = Logger.getLogger(EntityInFact.class);
    public static double CHANGE_ZONE_PROB = 0.05;
    public static double HISTOGRAM_DIFFERENCE_PENALIZATION = 9;
    private final Entity entity;
    private final Zone zone;
    private final TargetInfo entityInformation;
    private final double[] features;
    private final long lastDetection;

    public EntityInFact(Entity entity, Zone zone, double[] features, TargetInfo entityInformation, long lastDetection) {
        this.entity = entity;
        this.zone = zone;
        this.entityInformation = entityInformation;
        this.features = features;
        this.lastDetection = lastDetection;
    }

    public long getLastDetection() {
        return lastDetection;
    }

    public TargetInfo getEntityInformation() {
        return entityInformation;
    }

    public double[] getFeatures() {
        return features;
    }

    public Entity getEntity() {
        return entity;
    }

    public Zone getZone() {
        return zone;
    }

    public boolean detectionInGate(TDetection detection) {
        return detection.getZone().getId().equals(zone.getId()) || zone.getConnections().contains(detection.getZone().getId());
    }

    private void normalize(double[] vector) {
        double acc = 0;
        for (double d : vector) {
            acc += d;
        }
        double factor = 1 / acc;
        for (int i = 0; i < vector.length; i++) {
            vector[i] *= factor;
        }
    }

    private double bhattacharyyaMeasure(double[] v1, double[] v2) {
        double rslt = 0;
        for (int i = 0; i < v1.length; i++) {
            rslt += Math.sqrt(v1[i] * v2[i]);
        }
        return rslt;
    }

    public double detectionProbability(TDetection detection) {
        if (!detectionInGate(detection)) {
            return 0.0;
        }

        double rslt = probDetection(detection);
        if (zone.getConnections().contains(detection.getZone().getId())) {
            rslt *= CHANGE_ZONE_PROB;
        }
        return rslt;
    }

    private double probDetection(TDetection detection) {
        normalize(features);
        normalize(detection.getFeatures());

        double[] currentFeatures = features;
        double[] detectionFeatures = detection.getFeatures();

        double rslt = probDetectionCalc(currentFeatures, detectionFeatures);

        return rslt;
    }

    private double probDetectionCalc(double[] currentFeatures, double[] detectionFeatures) {
        double d = 1 - bhattacharyyaMeasure(currentFeatures, detectionFeatures);
        double rslt = 1 / ((d * HISTOGRAM_DIFFERENCE_PENALIZATION) + 1);
        return rslt;
    }

    @Override
    public String toString() {
        return "entity=" + entity + " zone=" + zone + " lastDetection=" + lastDetection;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final EntityInFact<TargetInfo> other = (EntityInFact<TargetInfo>) obj;
        if (this.entity != other.entity && (this.entity == null || !this.entity.equals(other.entity))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 19 * hash + (this.entity != null ? this.entity.hashCode() : 0);
        return hash;
    }
}
